/****************************************************************************
 *
 *              APM_emb.c -- APM embedded main file
 *
 *****************************************************************************
 * FileName:        APM_emb.c
 * Processor:       PIC18F
 * Creation date :	2009/11/07
 * Description :	APM embedded main file
 * Author :			Gilles KREBS
 * License :		Creative Commons 3.0 CC-BY-NC-SA
 *****************************************************************************/
/*****************************************************************************
 *	NOTES :
 * 			GK : UART = 9600 bauds
 *****************************************************************************/

/*****************************************************************************
 * INCLUDES
 *****************************************************************************/ 
 // LPIC
#include "MPPp18.h"
#include "GENp18.h"
#include "SYSp18.h"
#include "IOSp18.h"
#include "SPIp18.h"
#include "TTYp18.h"
#include "FSMp18.h"
#include "ADCp18.h"
#include "OWIp18_sw.h"
#include "M355dev.h"
//#include "LT08pic.h"
#include "YASKpic.h"
#include "DS18dev.h"
#include "OGIDdev.h"

// TRIM
#include "TRIMdefs.h"

// APM specific
#include "APM_emb.h"
#include "APM_hal.h"
#include "APM_com.h"

/*****************************************************************************
 * CONFIGURATION BITS
 *****************************************************************************/
// Device specific configuration
#if defined(__18F26K20)
	#pragma romdata CONFIG1H = 0x300001
	const rom unsigned char config1H = 0b00001000;      // HS clock
#elif defined(__18F2685)
	#pragma romdata CONFIG1H = 0x300001
	const rom unsigned char config1H = 0b00000010;      // HS clock
#else
	#error "Microcontroller not compatible with APM"
#endif

#pragma romdata CONFIG2L = 0x300002
const rom unsigned char config2L = 0b00011110;      // Brown-out Reset Enabled in hardware @ 2.0V, PWRTEN enabled

#pragma romdata CONFIG2H = 0x300003
const rom unsigned char config2H = 0b00010000;		// HW WD disabled, 1:32 prescaler

#pragma romdata CONFIG3H = 0x300005
const rom unsigned char config3H = 0b10000000;      // PORTB digital on RESET

#pragma romdata CONFIG4L = 0x300006
const rom unsigned char config4L = 0b11000001;      // DEBUG disabled,
	                                                // XINST enabled
	                                                // LVP disabled
	                                                // STVREN enabled
													
/*****************************************************************************
 * APM specific definitions
 *****************************************************************************/
// Applicative tasks definitions
#define APPmain		app_main

// Applicative messages definitions
#define VOLTAGE_MSG				64
#define CURRENT_MSG				65
#define TEMPERATURE_MSG			66
#define LCD_VOLT_MSG			67
#define LCD_CURR_MSG			68
#define LCD_TEMP_MSG			69
#define LCD_CLR_SCREEN			70
#define MPP_EVENT				71
#define SYSTEM_INITIALIZED		72
#define MODE_CLI				73
#define MODE_CRD				74
#define LCD_INIT_OK				75
#define LCD_MPP_MESSAGE			76
#define LCD_INITIALIZED			77
#define MPP_SERVICE_ACTIVATION	78
#define MPP_INIT_STATE1			79
#define MPP_INIT_STATE2			80
#define MPP_INIT_STATE3			81
#define LCD_MPP_CHANNEL			86

/*****************************************************************************
 * GLOBAL VARIABLES
 *****************************************************************************/
// MPP
#if ADDITIONAL_CONNECTION_PAYLOAD > 0
    BYTE AdditionalConnectionPayload[ADDITIONAL_CONNECTION_PAYLOAD];
#endif

SHORT 	myChannel 		= -1;
WORD 	myPan 			= 0xCAFE;
DWORD 	myChannelMap 	= 0x07FFF800;
BYTE	eui_data[8]		= {0xDE,0xAD,0xBE,0xEF,0xCA,0xFE,0xAA,0x55};
 
/*****************************************************************************
 * PROTOTYPES
 *****************************************************************************/
// Applicative tasks and services
SHORT	APPvoltage_task(YASK_MSG_T *mailbox);		// Voltage measurement task
SHORT	APPcurrent_task(YASK_MSG_T *mailbox);		// Current measurement task
SHORT	APPtemperature_task(YASK_MSG_T *mailbox);	// Temperature measurement task
SHORT	APPlcd_task(YASK_MSG_T *mailbox);			// LCD task
SHORT	APPmpp_task(YASK_MSG_T *mailbox);			// MPP task
SHORT	APPmpp_service(YASK_MSG_T *mailbox);		// MPP service

/*****************************************************************************
 * FUNCTIONS
 *****************************************************************************/
VOID APPmain(VOID)
{
    /*******************************************************************
	* Initialize the system
    *******************************************************************/	
    // Initialize hardware abstraction layer
	APM_hal_init();
		
	// Open and configure USART
	TTYopen(0,
			TTY_ASYNCH_MODE &
			TTY_EIGHT_BIT &
			TTY_CONT_RX &
			TTY_BRGH_HIGH,
			(DWORD)9600 );
				
	// Open and configure SPI 
	SPIopen( SPI_MOD_00, SPI_DIV_4);
							
	// Send welcome message on UART link
	TTYprintf( 0, (ROMS *) "Welcome to APM v%d.%d.%d with YASK\r\n", (WORD)APM_VERSION, (WORD)APM_COMPATIBILITY, (WORD)APM_RELEASE);
								
	/*******************************************************************
	* Register services and tasks
    *******************************************************************/
	
	// Register the services first and afterwards the tasks so the services will be stored
	//  at the beginning of the mailbox table and thus be called quicker during scheduling.
	APPmpp_service(NULL);
	
	// Print message
	TTYprintf( 0, (const rom char *) "APM : registering tasks to YASK...\r\n");
	
	// Initialize and register the tasks
	APPlcd_task(NULL);
	APPvoltage_task(NULL);
	APPcurrent_task(NULL);
	APPtemperature_task(NULL);
	APPmpp_task(NULL);
	
	// Print message
	TTYprintf( 0, (const rom char *) "APM : YASK initialized, let's go!\r\n");
	
} // End of main()

/*****************************************************************************
 * Voltage measurement task
 *****************************************************************************/
SHORT APPvoltage_task(YASK_MSG_T *mailbox) 
{
static char timer, mailbox_handle;
WORD raw_voltage;
FLOAT voltage;
WORD unites, centiemes;
 
	// After the first call, we need to manage the incoming mails
	if(mailbox)
	{
		switch(mailbox->idn)
		{
				case VOLTAGE_MSG:			
					// Read value
					raw_voltage = ADCread();
					
					// Convert to voltage
					voltage = (((raw_voltage * APM_REF_VOLTAGE) / 1024) * APM_VOLT_BRIDGE_DIV);
					
					// Convert to units + decimals
					unites = (WORD)voltage;
					centiemes = (WORD)((voltage-unites) * 1000);
					
					// Print value on UART
					TTYprintf( 0, (const rom char *) "Voltage     = %d.%dV\r\n", (WORD)unites, (WORD)centiemes);
					
					// Post result to LCD
					YASKmsg_post(mailbox_handle, LCD_VOLT_MSG, (ulong)raw_voltage, YASK_PRI_TSK);
					
					// Rearm measurement
					ADCstart(0);
					
					// Rearm the timer
					YASKtmr_start(timer, 500, VOLTAGE_MSG, YASK_PRI_TSK);
					break;
				default:
					break;		
		}
	}
	else // First call with NULL pointer : initialization
	{
		// Open and configure integrated ADC
		ADCinit( ADC_CFG_DDDDDDDDDDA , ADC_TAD_16, ADC_DIV_16);
	
		// Register an auto-reload timer
		timer = YASKtmr_register(APPvoltage_task, (roms *) "VltTmr", 0);
		
		// Start measurement
		ADCstart(0);
		
		// Start the timer
		YASKtmr_start(timer, 500, VOLTAGE_MSG, YASK_PRI_TSK);
		
		// Register a mailbox to send data to LCD task
		mailbox_handle = YASKmbx_register(APPlcd_task, (roms *) "LcdVlt");
		
	}

} // End of APPvoltage_task()

/*****************************************************************************
 * Current measurement task
 *****************************************************************************/
SHORT APPcurrent_task(YASK_MSG_T *mailbox) 
{
static char timer, mailbox_handle;
DSHORT uvolt, current;
 
	// After the first call, we need to manage the incoming mails
	if(mailbox)
	{
		switch(mailbox->idn)
		{
				case CURRENT_MSG:
					// Read voltage on external ADC
					M355read_uvolt( TRUE, &uvolt);
					
					// Converts the current
					current = uvolt * (1.0E-3 / APM_SHUNT);
					
					// Print value on UART
					TTYprintf( 0, (const rom char *) "Current     = %ldmA\r\n", current);
					
					// Post result to LCD
					YASKmsg_post(mailbox_handle, LCD_CURR_MSG, (ulong)uvolt, YASK_PRI_TSK);
					
					// Start the timer
					YASKtmr_start(timer, 500, CURRENT_MSG, YASK_PRI_TSK);
					break;
				default:
					break;		
		}
	}
	else // First call with NULL pointer : initialization
	{
		// Open and configure the MCP3550 converter
		M355open(IOS_PORT_C, IOS_RX0, (FLOAT)1.25);
	
		// Register an auto-reload timer
		timer = YASKtmr_register(APPcurrent_task, (roms *) "CurTmr", 0);
		
		// Start the timer
		YASKtmr_start(timer, 500, CURRENT_MSG, YASK_PRI_TSK);
		
		// Register a mailbox to send data to LCD task
		mailbox_handle = YASKmbx_register(APPlcd_task, (roms *) "LcdCur");
	}

} // End of APPcurrent_task()

/*****************************************************************************
 * Temperature measurement task
 *****************************************************************************/
SHORT APPtemperature_task(YASK_MSG_T *mailbox) 
{
static char timer, mailbox_handle;
BYTE i;
WORD units, decimals;
static BYTE romcode[8];
static BYTE scratchpad[9];
ulong temp;
FLOAT temperature;
 
	// After the first call, we need to manage the incoming mails
	if(mailbox)
	{
		switch(mailbox->idn)
		{
				case TEMPERATURE_MSG:
					// Get temperature
					YASKstop(TRUE);
					temperature = DS18get_temperature(FALSE, NULL);
					YASKstop(FALSE);
					
					// Convert to units + decimals
					units = (WORD)temperature;
					decimals = (WORD)((temperature-units) * 1000);

					TTYprintf(0,"Temperature = %d.%d C\r\n",(WORD)units, (WORD)decimals);
					
					// Post result to LCD
					memcpy(&temp, (void *)&temperature, 4);
					YASKmsg_post(mailbox_handle, LCD_TEMP_MSG, temp, YASK_PRI_TSK);

					// Start new conversion	
					YASKstop(TRUE);					
					DS18start_conversion(FALSE, NULL);
					YASKstop(FALSE);
					
					// Rearm the timer
					YASKtmr_start(timer, 2000, TEMPERATURE_MSG, YASK_PRI_TSK);
					break;
				default:
					break;		
		}
	}
	else // First call with NULL pointer : initialization
	{
		// Open and configure DS18B20
		DS18open(IOS_PORT_A, IOS_RX3);
	
		// Start conversion		
		YASKstop(TRUE);
		DS18start_conversion(FALSE, NULL);
		YASKstop(FALSE);
	
		// Register an auto-reload timer
		timer = YASKtmr_register(APPtemperature_task, (roms *) "TmpTmr", 0);
		
		// Start the timer
		YASKtmr_start(timer, 2000, TEMPERATURE_MSG, YASK_PRI_TSK);
		
		// Register a mailbox to send data to LCD task
		mailbox_handle = YASKmbx_register(APPlcd_task, (roms *) "LcdTmp");
	}

} // End of APPtemperature_task()

/*****************************************************************************
 * LCD task
 *****************************************************************************/
SHORT APPlcd_task(YASK_MSG_T *mailbox) 
{
//static LT08 lt08_lcd;
static OGID oled_display;
static char timer;
static BOOL lcd_initialized, sys_initialized;
FLOAT voltage;
WORD current;
WORD units, decimals;
FLOAT temperature;
 
	// After the first call, we need to manage the incoming mails
	if(mailbox)
	{
		switch(mailbox->idn)
		{
			case LCD_VOLT_MSG:
				if(sys_initialized)
				{
					// Convert to voltage
					voltage = (((mailbox->eid * APM_REF_VOLTAGE) / 1024) * APM_VOLT_BRIDGE_DIV);
					
					// Convert to units + decimals
					units = (WORD)voltage;
					decimals = (WORD)((voltage-units) * 100);
					
					// Print on the LCD
					// LT08print_text(&lt08_lcd, 1, 1, "%d.%dV", (WORD) units, (WORD)decimals);
					OGIDprintf(&oled_display, 1, 4, (ROM CHAR*)"Voltage=%02d.%dV", (WORD) units, (WORD)decimals);
				}
				break;
			case LCD_CURR_MSG:
				if(sys_initialized)
				{
					// Converts the current
					current = (WORD)(mailbox->eid * (1.0E-3 / APM_SHUNT));
					
					// Print on the LCD
					// LT08print_text(&lt08_lcd, 1, 9, "%dmA", (WORD) current);
					OGIDprintf(&oled_display, 2, 4, (ROM CHAR*)"Current=%03dmA", (WORD) current);
				}
				break;
			case LCD_TEMP_MSG:
				if(sys_initialized)
				{
					// Convert temperature to units + decimals
					memcpy(&temperature, (void *)&mailbox->eid, 4);
					units = (WORD)temperature;
					decimals = (WORD)((temperature - units) * 10);
				
					// Print on the LCD
					// LT08print_text(&lt08_lcd, 2, 1, "%02d.%d C", (WORD)units, (WORD)decimals);
					OGIDprintf(&oled_display, 3, 4, (ROM CHAR*)"Temperature=%02d.%d C", (WORD)units, (WORD)decimals);
				}
				break;
			case LCD_CLR_SCREEN:
				// LT08clear_screen(&lt08_lcd);
				OGIDclear_screen(&oled_display);
				break;
			case LCD_MPP_MESSAGE:
				switch(mailbox->eid)
				{
					case MODE_CLI :	// CLIent mode selected
						// LT08print_text(&lt08_lcd, 1, 1, "CLIent mode     ");
						OGIDprintf(&oled_display, 1, 4, (ROM CHAR*)"CLIent mode     ");
						// LT08print_text(&lt08_lcd, 2, 1, "Active scanning ");
						OGIDprintf(&oled_display, 2, 4, (ROM CHAR*)"Active scanning ");
						break;
					case MODE_CRD :	// CooRDinator mode
						// LT08print_text(&lt08_lcd, 1, 1, "CooRDinator mode");
						OGIDprintf(&oled_display, 1, 4, (ROM CHAR*)"CooRDinator mode");
						// LT08print_text(&lt08_lcd, 2, 1, "Energy scanning ");
						OGIDprintf(&oled_display, 2, 4, (ROM CHAR*)"Energy scanning ");
						break;
					default :
						break;
				}
				break;
			case SYSTEM_INITIALIZED :
				// LT08clear_screen(&lt08_lcd);
				OGIDclear_screen(&oled_display);
				sys_initialized = TRUE;
				break;
			default:
				break;		
		}
	}
	else // First call with NULL pointer : initialization
	{
		// Reset the "LCD initialized" and "System initialized" statuses
		sys_initialized = FALSE;
		
		// Open and configure LCD
		// LT08open(&lt08_lcd, 2, 16, 0x40, 
							// LCD_RS, LCD_RW, LCD_E, LCD_BL, 
							// LCD_D7, LCD_D6, LCD_D5, LCD_D4);
		OGIDopen(&oled_display, 0x7A, 32, 128);
							
		// Send welcome message on LCD
		// LT08print_text(&lt08_lcd, 1, 1, (ROM CHAR*)"    WELCOME     ");
		OGIDprintf(&oled_display, 1, 4, (ROM CHAR*)"    WELCOME     ");
		// LT08print_text(&lt08_lcd, 2, 1, (ROM CHAR*)"   v%d.%d.%d        ", (WORD)APM_VERSION, (WORD)APM_COMPATIBILITY, (WORD)APM_RELEASE);
		OGIDprintf(&oled_display, 2, 4, (ROM CHAR*)"   v%d.%d.%d        ", (WORD)APM_VERSION, (WORD)APM_COMPATIBILITY, (WORD)APM_RELEASE);
		
		// Activate backlight
		// LT08backlight(&lt08_lcd, TRUE);	
		OGIDswitch_display_on(&oled_display, TRUE);		
	}

} // End of APPtemperature_task()

/*****************************************************************************
 * MPP task
 *****************************************************************************/
SHORT APPmpp_task(YASK_MSG_T *mailbox)
{
static char lcd_mailbox_handle, mpp_mailbox_handle;
static char timer;
static BOOL client;
static BYTE mode;
static DWORD tmpChannelMap;
ulong temp;

	if(mailbox)
	{
		switch(mailbox->idn)
		{
			case MPP_INIT_STATE1 :
				TTYprintf(0, (const rom char *)"STATE1\r\n");
				// Initialize MPP stack
				MPPopen( eui_data, MPP_INT_INT0, TRUE, 
					IOS_PORT_C, IOS_RX1, // Chip select
					IOS_PORT_C, IOS_RX2, // Wake
					IOS_PORT_B, IOS_RX5, // Reset
					FALSE,	// No PA_LNA
					FALSE); // No turbo
			
				// Reload timer for next state (10ms later)
				YASKtmr_start(timer, 10, MPP_INIT_STATE2, YASK_PRI_TSK);
			
				// Clear the LCD
				YASKmsg_post(lcd_mailbox_handle, LCD_CLR_SCREEN, NULL, YASK_PRI_TSK);
				break;
			case MPP_INIT_STATE2 :
				TTYprintf(0, (const rom char *)"STATE2\r\n");

				// In case of CRD mode, first perform an active scan to verify that the network does not exist
				if(((myChannel = MPPsearch(myPan, MPP_SEARCH_ACTIVE, myChannelMap, 6)) < 0) && !client)
				{
					// Temporary solution ==> to be modified!!
					eui_data[7] = 0x02;
					
					// Re-open MPP with new network EUI.
					MPPopen( eui_data, MPP_INT_INT0, TRUE,
						IOS_PORT_C, IOS_RX1, // Chip select
						IOS_PORT_C, IOS_RX2, // Wake
						IOS_PORT_B, IOS_RX5, // Reset
						FALSE,	// No PA_LNA
						FALSE); // No turbo
						
					// Reload timer for next state (10ms later)
					YASKtmr_start(timer, 10, MPP_INIT_STATE3, YASK_PRI_TSK);
					
					// Send message to the LCD
					YASKmsg_post(lcd_mailbox_handle, LCD_MPP_MESSAGE, MODE_CRD, YASK_PRI_TSK);
				}
				else
				{
					// If a PAN has been found => automatically become a client
					client = 1;
					
					// If a PAN has been found, connect directly to it
					if(myChannel > 0)
					{
						mode = MPP_JOIN_DIRECT|MPP_BLOCK;
						tmpChannelMap = (DWORD)myChannel;
					}
					else // No PAN found, so scan until a PAN has been found
					{
						mode = MPP_JOIN_ACTIVE|MPP_BLOCK;
						tmpChannelMap = myChannelMap;
					}
					
					// Reload timer for next state (10ms later)
					YASKtmr_start(timer, 10, MPP_INIT_STATE3, YASK_PRI_TSK);
					
					// Send message to the LCD
					YASKmsg_post(lcd_mailbox_handle, LCD_MPP_MESSAGE, MODE_CLI, YASK_PRI_TSK);
				}
				break;
			case MPP_INIT_STATE3 :
				TTYprintf(0, (const rom char *)"STATE3\r\n");
				if(client)
					{
					// try to join the PAN until success
					myChannel = MPPjoin(myPan, mode, tmpChannelMap, 6);
					}
				else
					{
					// Create network with "energy scan" method
					myChannel = MPPcreate(myPan, MPP_CREATE_ENERGY, myChannelMap, 10);
					}
				
				// Init done
				YASKmsg_post(lcd_mailbox_handle, SYSTEM_INITIALIZED, NULL, YASK_PRI_TSK);					
				break;
			default :
				break;
		}
	}
	else
	{
		// Get init status
		client = APM_BTN_FUNC;
	
		// Register a mailbox to send messages to the LCD task		
		lcd_mailbox_handle = YASKmbx_register(APPlcd_task, (roms *) "MptLcd");
		
		// Register a mailbox to send messages to the MPP services
		mpp_mailbox_handle = YASKmbx_register(APPmpp_service, (roms *) "MptMps");
		
		// Register a timer to start MPP initialization
		timer = YASKtmr_register(APPmpp_task, (roms *) "MptTmr", 0);
			
		// Start the timer 500ms after init
		YASKtmr_start(timer, 500, MPP_INIT_STATE1, YASK_PRI_TSK);
	}
}

/*****************************************************************************
 * MPP service
 *****************************************************************************/
SHORT APPmpp_service(YASK_MSG_T *mailbox)
{
static char timer, mpp_mailbox_handle;
static BOOL mpp_service_active;

	if(mailbox)
	{
		switch(mailbox->idn)
		{
			case MPP_EVENT :

				break;
			default :
				break;
		}
	}
	else
	{
		// When initialized, service is inactive
		mpp_service_active = FALSE;
	
		// I register an auto reload polling timer
		timer = YASKtmr_register(APPmpp_service, (roms *) "MpsTmr", 10);
		
		// Start the timer 
		YASKtmr_start(timer, 0, MPP_EVENT, YASK_PRI_TSK);
		
		// Register a mailbox to send messages to the MPP task		
		mpp_mailbox_handle = YASKmbx_register(APPmpp_task, (roms *) "MppEvt");
	}
}

